<?php
/**
 * @link https://ym2.cleverstone.top
 * @copyright Copyright (c) 2020 Yii Manager Software LLC
 */

namespace common\widgets;

use Yii;
use yii\base\UserException;
use yii\base\Widget;
use yii\helpers\ArrayHelper;
use common\models\Admin;
use builder\helper\H5;
use builder\helper\Url;

/**
 * 菜单小部件
 * @author cleverstone
 * @since ym2.0
 */
class Menu extends Widget
{
    /**
     * @var string 当前路由
     */
    public $route;
    /**
     * 菜单项模板
     * @var string
     */
    public $menuItemTemplate = <<<TPL
    <li class="layui-nav-item{uiClass}">
        <a href="{link}" target="{target}">
            <span class="inline-block w-20 text-center{icon}"></span>
            {label}
        </a>
        {children}
    </li>
TPL;
    /**
     * 子菜单模板
     * @var string
     */
    public $submenuTemplate = <<<TPL
    <dl class="layui-nav-child">
        {items}
    </dl>
TPL;
    /**
     * 子菜单项模板
     * @var string
     */
    public $submenuItemTemplate = <<<TPL
    <dd class="{uiClass}">
        <a href="{link}" target="{target}">
            {label}
        </a>
        {children}
    </dd>
TPL;
    /**
     * @var array 菜单数据
     */
    private $_menuData = [];

    /**
     * Initialize
     * @throws UserException
     */
    public function init()
    {
        parent::init();
        if ($this->route === null && Yii::$app->controller !== null) {
            $this->route = Yii::$app->controller->getRoute();
        }

        $this->initMenuData();
    }

    /**
     * @throws UserException
     */
    protected function initMenuData()
    {
        /* @var Admin $identity */
        $identity = Yii::$app->user->identity;
        if (empty($identity)) {
            throw new UserException('No authorization');
        }

        $permissions = Yii::$app->rbacManager->loadPermissionsFromCache($identity->group);
        $permissionGroup = ArrayHelper::index($permissions, null, 'category');
        $data = !empty($permissionGroup['MENU']) ? $permissionGroup['MENU'] : [];

        $menuData = array_merge(
            $this->getPublicTopMenu(),
            $this->normalizeData($data),
            $this->getPublicBottomMenu()
        );

        $this->switchItem($menuData);
        $this->_menuData = $menuData;
    }

    /**
     * @return array
     */
    protected function getPublicTopMenu()
    {
        return require Yii::getAlias('@backend/menu/public.top.php');
    }

    /**
     * @return array
     */
    protected function getPublicBottomMenu()
    {
        return require Yii::getAlias('@backend/menu/public.bottom.php');
    }

    public function run()
    {
        /* ul --begin */
        echo H5::beginTag('ul', ['class' => 'ym2-main-menu layui-nav layui-nav-tree layui-nav-side']) . "\n";

        echo $this->render('menu_header');

        echo $this->renderMenuItems() . "\n";
        /* ul --end */
        echo H5::endTag('ul') . "\n";
    }

    /**
     * 渲染菜单项
     * @return string
     */
    public function renderMenuItems()
    {
        $lines = [];
        foreach ($this->_menuData as $value) {
            $uiClass = '';
            if (!empty($value['is_active'])) {
                $uiClass = ' layui-this';
                if (!empty($value['children'])) {
                    $uiClass = ' layui-nav-itemed';
                }
            }

            $link = Url::to($value['link'], '');
            if (!empty($value['children'])) {
                $link = 'javascript:;';
            }

            $menu = strtr($this->menuItemTemplate, [
                '{uiClass}' => $uiClass,
                '{link}' => $link,
                '{target}' => !empty($value['dump_way']) ? $value['dump_way'] : '_self',
                '{icon}' => !empty($value['icon']) ? (' ' . $value['icon']) : '',
                '{label}' => $value['name'],
                '{children}' => !empty($value['children']) ? strtr($this->submenuTemplate, [
                    '{items}' => $this->renderChildren($value['children']),
                ]) : '',
            ]);

            $lines[] = $menu;
        }

        return implode("\n", $lines);
    }

    /**
     * 渲染子菜单
     * @param array $children
     * @return string
     */
    public function renderChildren($children)
    {
        $childrenLines = [];
        foreach ($children as $value) {
            $uiClass = '';
            if (!empty($value['is_active'])) {
                $uiClass = 'layui-this';
                if (!empty($value['children'])) {
                    $uiClass = 'layui-nav-itemed';
                }
            }

            $link = Url::to($value['link'], '');
            if (!empty($value['children'])) {
                $link = 'javascript:;';
            }

            $submenu = strtr($this->submenuItemTemplate, [
                '{uiClass}' => $uiClass,
                '{link}' => $link,
                '{target}' => !empty($value['dump_way']) ? $value['dump_way'] : '_self',
                '{label}' => $value['name'],
                '{children}' => !empty($value['children']) ? strtr($this->submenuTemplate, [
                    '{items}' => $this->renderChildren($value['children']),
                ]) : '',
            ]);

            $childrenLines[] = $submenu;
        }

        return implode("\n", $childrenLines);
    }

    /**
     * 标准化数据
     * @param array $data 菜单数据
     * @param int $pid PID
     * @return array
     */
    public function normalizeData($data, $pid = 0)
    {
        $result = [];
        foreach ($data as $i => $item) {
            if ($item['pid'] == $pid) {
                unset($data[$i]);

                $item['children'] = $this->normalizeData($data, $item['id']);

                array_push($result, [
                    'name' => $item['name'],
                    'icon' => $item['icon'],
                    'link' => $item['link'],
                    'dump_way' => $item['dump_way'],
                    'children' => $item['children'],
                ]);
            }
        }

        return $result;
    }

    /**
     * 切换菜单项
     * @param array $data 标准化后的菜单数据
     * @param bool $isActive
     * @return array
     */
    public function switchItem(&$data, &$isActive = false)
    {
        $result = [];
        foreach ($data as &$item) {

            $hasActiveChild = false;
            if (!empty($item['children'])) {
                $this->switchItem($item['children'], $hasActiveChild);
            }

            if (!strcmp($item['link'], $this->route) || $hasActiveChild) {
                $isActive = $item['is_active'] = true;
            } else {
                $item['is_active'] = false;
            }
        }

        return $result;
    }
}